package com.comall.dc.banktest.controller;

import com.comall.dc.banktest.application.AccountApplication;
import com.comall.dc.banktest.application.command.*;
import com.comall.dc.banktest.controller.vo.AccountRecordVO;
import com.comall.dc.banktest.controller.vo.AccountVO;
import com.comall.dc.banktest.domain.exception.*;
import com.comall.dc.banktest.domain.model.Account;
import com.comall.dc.banktest.domain.model.AccountRecord;
import com.comall.dc.banktest.query.AccountQuery;
import com.comall.dc.banktest.query.AccountRecordQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/v1/base/accounts")
@Api(value = "账户RestfulApi", tags = "账户RestfulApi")
public class AccountController {

    @Autowired
    private AccountApplication accountApplication;

    @Autowired
    private AccountQuery accountQuery;

    @Autowired
    private AccountRecordQuery accountRecordQuery;



    @GetMapping("")
    @ApiOperation("查询账户列表")
    public List<AccountVO> list(
            @RequestParam(defaultValue = "min") LocalDateTime beginTime,
            @RequestParam(defaultValue = "max") LocalDateTime endTime,
            @RequestParam(defaultValue = "") String likeId,
            @RequestParam(defaultValue = "")String likeIdCard,
            @RequestParam(defaultValue = "")String likeUserPhone,
            HttpServletResponse response) throws IOException {
        try {
            response.setStatus(HttpServletResponse.SC_OK);
            return accountQuery.listAccountByTimeRangeAndLikeIdAndLikeIdCardAndLikeUserPhone(beginTime, endTime, likeId, likeIdCard, likeUserPhone).stream().map(i->{
                AccountVO accountVO = new AccountVO();
                BeanUtils.copyProperties(i, accountVO);
                return accountVO;
            }).collect(Collectors.toList());
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return new ArrayList<>();
        }

    }

    @PostMapping("")
    @ApiOperation("账户开户")
    public Account create(
            @RequestParam String idCard,
            @RequestParam String userName,
            @RequestParam String userPhone,
            @RequestParam String userJob,
            @RequestParam String userAddress,
            HttpServletResponse response) throws IOException {
        try {
            CreateAccountCommand command = new CreateAccountCommand(idCard, userName, userPhone, userAddress, userJob);
            response.setStatus(HttpServletResponse.SC_CREATED);
            return accountApplication.createAccount(command);


        } catch (IdCardExistException e) {
            response.sendError(HttpServletResponse.SC_CONFLICT);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @PutMapping("/{id}/password")
    @ApiOperation("账户修改密码激活")
    public Account changePassword(
            @PathVariable("id") Integer id,
            @RequestParam String idCard,
            @RequestParam String password,
            HttpServletResponse response) throws IOException {
        try {
            ActivateAccountCommand command = new ActivateAccountCommand(id, idCard, password);
            response.setStatus(HttpServletResponse.SC_ACCEPTED);
            return accountApplication.activateAccount(command);
        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @PutMapping("/{id}/freeze")
    @ApiOperation("账户冻结")
    public Account freeze(@PathVariable("id")Integer id, HttpServletResponse response) throws IOException {

        try {
            FreezeAccountCommand command = new FreezeAccountCommand(id);
            response.setStatus(HttpServletResponse.SC_OK);
            return accountApplication.freezeAccount(command);
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }

    }

    @GetMapping("/{id}/balance")
    @ApiOperation("查询账户余额")
    public BigDecimal getBalance(
            @PathVariable("id")Integer id,
            @RequestParam String password,
            HttpServletResponse response) throws IOException {
        try {
            response.setStatus(HttpServletResponse.SC_OK);
            Account account = accountQuery.getById(id);
            if (account.isAuthPass(password)){
                return account.getBalance();
            } else {
                throw new AccountNoAuthException();
            }

        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @PutMapping("/{id}/balance")
    @ApiOperation("存款")
    public BigDecimal depositBalance(
            @PathVariable("id")Integer id,
            @RequestParam String password,
            @RequestParam BigDecimal value,
            HttpServletResponse response) throws IOException {
        try {
            DepositBalanceCommand command = new DepositBalanceCommand(id, password, value);
            response.setStatus(HttpServletResponse.SC_OK);
            return accountApplication.depositBalance(command);
        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        } catch (AccountFreezeException e) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @DeleteMapping("/{id}/balance")
    @ApiOperation("取款")
    public BigDecimal drawBalance(
            @PathVariable("id")Integer id,
            @RequestParam String password,
            @RequestParam BigDecimal value,
            HttpServletResponse response) throws IOException {
        try {
            DrawBalanceCommand command = new DrawBalanceCommand(id, password, value);
            response.setStatus(HttpServletResponse.SC_OK);
            return accountApplication.drawBalance(command);
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        } catch (AccountFreezeException e) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (BalanceInsufficientException e) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @DeleteMapping("/{id}/balance/{target-id}")
    @ApiOperation("转账")
    public BigDecimal transferBalance(
            @PathVariable("id")Integer id,
            @PathVariable("target-id") Integer targetId,
            @RequestParam BigDecimal value,
            @RequestParam String password,
            HttpServletResponse response) throws IOException {

        try {
            TransferBalanceCommand command = new TransferBalanceCommand(id, password, targetId, value);
            response.setStatus(HttpServletResponse.SC_OK);
            return accountApplication.transferBalance(command);
        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        } catch (AccountFreezeException e) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (BalanceInsufficientException e) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }

    @GetMapping("/{id}/record")
    @ApiOperation("查询账户账单")
    public List<AccountRecordVO> listRecord(
            @PathVariable("id")Integer id,
            @RequestParam String password,
            @RequestParam(defaultValue = "min") LocalDateTime beginTime,
            @RequestParam(defaultValue = "max") LocalDateTime endTime,
            HttpServletResponse response) throws IOException {

        try {
            accountApplication.validPassword(id, password);
            response.setStatus(HttpServletResponse.SC_OK);
            return accountRecordQuery.listByAccountIdAndTimeRange(id, beginTime, endTime).stream().map(i->{
                AccountRecordVO accountRecordVO = new AccountRecordVO();
                BeanUtils.copyProperties(i, accountRecordVO);
                return accountRecordVO;
            }).collect(Collectors.toList());

        } catch (AccountNotExistException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return new ArrayList<>();
        } catch (AccountNoAuthException e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return new ArrayList<>();
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return new ArrayList<>();
        }
    }




    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new LocalDateTimeEditor());
    }

    private class LocalDateTimeEditor extends PropertyEditorSupport {
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text.equals("min")){
                setValue(LocalDateTime.of(1900, 1, 1, 0, 0, 0));
            }
            else if (text.equals("max")){
                setValue(LocalDateTime.of(3000, 12, 31, 23, 59, 59));
            } else {
                super.setAsText(text);
            }
        }
    }
}
